package cn.hutool.core.comparator;

import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ObjectUtil;

import java.io.Serializable;
import java.util.Comparator;

/**
 * 汉字拼音加版本比较器
 * 字符串有汉字的时候，汉字部分排序结果同PinyinComparator排序结果，数字部分排序结果同VersionComparator排序结果
 *
 * @since 2023/10/09 18:39
 */
public class PinyinAndVersionComparator implements Comparator<String>, Serializable {

	private static final long serialVersionUID = 7520224992810502033L;

	private String str1, str2;
	private int pos1, pos2, len1, len2;

	/**
	 * 单例
	 */
	public static final PinyinAndVersionComparator INSTANCE = new PinyinAndVersionComparator();

	/**
	 * 默认构造
	 */
	public PinyinAndVersionComparator() {
	}

	@Override
	public int compare(String o1, String o2) {

		if (ObjectUtil.equal(o1, o2)) {
			return 0;
		}
		if (o1 == null && o2 == null) {
			return 0;
		} else if (o1 == null) {// null视为最小版本，排在前
			return -1;
		} else if (o2 == null) {
			return 1;
		}
		str1 = o1;
		str2 = o2;
		len1 = str1.length();
		len2 = str2.length();
		pos1 = pos2 = 0;

		int result = 0;
		while (result == 0 && pos1 < len1 && pos2 < len2) {
			char ch1 = str1.charAt(pos1);
			char ch2 = str2.charAt(pos2);

			if (CharUtil.isNumber(ch1)) {
				result = CharUtil.isNumber(ch2) ? compareNumbers() : -1;
			} else if (CharUtil.isLetter(ch1)) {
				result = CharUtil.isLetter(ch2) ? compareOther(true) : 1;
			} else {
				result = CharUtil.isNumber(ch2) ? 1
						: CharUtil.isLetter(ch2) ? -1
						: compareOther(false);
			}
			pos1++;
			pos2++;
		}

		return result == 0 ? len1 - len2 : result;
	}

	private int compareNumbers() {
		int end1 = pos1 + 1;
		while (end1 < len1 && CharUtil.isNumber(str1.charAt(end1))) {
			end1++;
		}
		int fullLen1 = end1 - pos1;
		while (pos1 < end1 && str1.charAt(pos1) == '0') {
			pos1++;
		}

		int end2 = pos2 + 1;
		while (end2 < len2 && CharUtil.isNumber(str2.charAt(end2))) {
			end2++;
		}
		int fullLen2 = end2 - pos2;
		while (pos2 < end2 && str2.charAt(pos2) == '0') {
			pos2++;
		}

		int delta = (end1 - pos1) - (end2 - pos2);
		if (delta != 0) {
			return delta;
		}

		while (pos1 < end1 && pos2 < end2) {
			delta = str1.charAt(pos1++) - str2.charAt(pos2++);
			if (delta != 0) {
				return delta;
			}
		}

		pos1--;
		pos2--;

		return fullLen2 - fullLen1;
	}

	private int compareOther(boolean isLetters) {
		char ch1 = str1.charAt(pos1);
		char ch2 = str2.charAt(pos2);

		if (ch1 == ch2) {
			return 0;
		}

		if (isLetters) {
			ch1 = Character.toUpperCase(ch1);
			ch2 = Character.toUpperCase(ch2);
			if (ch1 != ch2) {
				ch1 = Character.toLowerCase(ch1);
				ch2 = Character.toLowerCase(ch2);
			}
		}
		return new PinyinComparator().compare(String.valueOf(ch1), String.valueOf(ch2));
	}

}
